package com.aliyun.flashsale.portal.service.impl;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Date;
import java.util.UUID;

import com.alibaba.fastjson.JSON;

import com.aliyun.commons.model.Result;
import com.aliyun.commons.service.CacheService;
import com.aliyun.commons.service.SendMessageService;
import com.aliyun.flashsale.portal.model.ProductInfoDTO;
import com.aliyun.flashsale.portal.service.CreateOrderService;
import com.aliyun.flashsale.portal.service.ProductQueryService;
import com.aliyun.flashsale.portal.utils.ExceptionUtils;
import com.aliyun.inventory.client.constants.InventoryCacheKey;
import com.aliyun.order.client.model.CreateOrderRequest;
import com.aliyun.order.client.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

/**
 * 创建 Order Service 实现
 *
 * 
 * @date 2024/11/28
 */
@Service
@Slf4j
public class CreateOrderServiceImpl implements CreateOrderService {

    @Autowired
    private CacheService cacheService;
    @DubboReference
    private OrderService orderService;
    @Autowired
    private ProductQueryService productQueryService;
    @Autowired
    private SendMessageService sendMessageService;
    @Value("${rocketmq.order-fail-after-pre-deducted-inventory-topic}")
    private String topic;

    /**
     * 扣减库存脚本
     */
    private static String REDUCTION_INVENTORY_SCRIPT = "";
    /**
     * 扣减库存 LUA 脚本
     */
    private static final String REDUCTION_INVENTORY_LUA = "reduction_inventory.lua";

    static {
        // 读取脚本文件
        ClassPathResource classPathResource = new ClassPathResource(REDUCTION_INVENTORY_LUA);
        try {
            REDUCTION_INVENTORY_SCRIPT = IOUtils.toString(classPathResource.getInputStream(),
                StandardCharsets.UTF_8.name());
            REDUCTION_INVENTORY_SCRIPT = REDUCTION_INVENTORY_SCRIPT.replaceAll("--.*", "").replaceAll(
                "(?m)^\\s*\\r?\\n+", "");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public long createOrder(long userId, long flashSaleProductId, int quantity) {
        // 1. 校验参数
        ProductInfoDTO productInfoDTO = validateParams(flashSaleProductId, quantity);
        Long productId = productInfoDTO.getProductId();

        // 2. 从缓存中预扣除库存
        preDeductInventoryFromCache(productId, userId, quantity, productInfoDTO.getMaxPurchasePerUser());

        // 3，创建订单
        return createRemoteOrder(flashSaleProductId, productId, userId, quantity);
    }

    /**
     * 验证参数
     *
     * @param flashSaleProductId 商品 ID
     * @param quantity           数量
     */
    private ProductInfoDTO validateParams(long flashSaleProductId, int quantity) {
        if (quantity <= 0) {
            throw new RuntimeException("商品购买件数必须大于0");
        }
        ProductInfoDTO productInfo = productQueryService.getProductInfo(flashSaleProductId);
        if (!ProductInfoDTO.ONLINE.equals(productInfo.getStatus())) {
            throw new RuntimeException("商品未上架");
        }
        Date now = new Date();
        if (now.before(productInfo.getStartTime()) || now.after(productInfo.getEndTime())) {
            throw new RuntimeException("当前时间不在商品秒杀时间段内");
        }
        if (productInfo.getRemainQuantity() <= 0) {
            throw new RuntimeException("商品已售罄");
        }
        if (quantity > productInfo.getRemainQuantity()) {
            throw new RuntimeException("商品库存不足");
        }
        return productInfo;
    }

    /**
     * 创建远程订单
     *
     * @param productId 商品 ID
     * @param userId    用户 ID
     * @param quantity  数量
     * @return
     */
    private Long createRemoteOrder(long flashSaleProductId, long productId, long userId, int quantity) {
        CreateOrderRequest request = new CreateOrderRequest();
        request.setProductId(productId);
        request.setUserId(userId);
        request.setQuantity(quantity);
        request.setFlashSaleProductId(flashSaleProductId);
        try {
            Result<Long> createResult = orderService.createOrder(request);
            if (!createResult.isSuccess() || createResult.getData() == null || createResult.getData() <= 0) {
                throw new RuntimeException(createResult.getMessage());
            }
            return createResult.getData();
        } catch (Throwable e) {
            String errorMessage = ExceptionUtils.getRootCauseMessage(e, "创建订单异常");
            log.error("create order failed, send fail message to mq, params={} message={}", JSON.toJSONString(request),
                errorMessage, e);
            // 发送失败消息
            sendFailMessage(request);
            throw new RuntimeException(errorMessage);
        }
    }

    /**
     * 异步发送消息
     *
     * @param request 请求
     */
    private void sendFailMessage(CreateOrderRequest request) {
        String messageKey = UUID.randomUUID() + "_" + request.getUserId() + "_" + request.getProductId();
        sendMessageService.sendAsyncMessage(topic, null, messageKey, JSON.toJSONString(request));
    }

    /**
     * 从缓存中预扣除库存
     *
     * @param productId          商品 ID
     * @param userId             用户 ID
     * @param quantity           数量
     * @param maxPurchasePerUser
     */
    public void preDeductInventoryFromCache(long productId, long userId, int quantity, int maxPurchasePerUser) {
        String inventoryKey = InventoryCacheKey.buildRemainQuantityKey(productId);
        String userPurchasedKey = InventoryCacheKey.buildUserPurchasedQuantityKey(productId, userId);
        long result = cacheService.executeLuaScript(REDUCTION_INVENTORY_SCRIPT,
            Arrays.asList(inventoryKey, userPurchasedKey), quantity, maxPurchasePerUser);
        if (result == -1) {
            throw new RuntimeException("购买件数无效");
        }
        if (result == -2) {
            throw new RuntimeException("库存数据不存在");
        }
        if (result == -3 || result == -4) {
            throw new RuntimeException("库存不足");
        }
        if (result == -5) {
            throw new RuntimeException("超过限购上限");
        }
    }
}